package com.miuugo.common.tools;



import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author shaopeng.hu
 */
public class BatchUtil {

    /**
     * 用于collection分批执行
     *
     * @param collection collection
     * @param step       批次容量
     * @param total      总批次数
     * @param sync       是否同步
     * @param collector  分割收集器
     * @param consumer   consumer
     */
    private static <E, A, R> void batch(Collection<E> collection, int step, int total, boolean sync, Collector<E, A, R> collector, Consumer<R> consumer) {
        IntStream stream = IntStream.iterate(0, n -> n + 1)
                .limit(total);
        // 异步
        if (!sync) {
            stream = stream.parallel();
        }
        stream.forEach(num ->
                consumer.accept(collection.stream()
                        .skip(num * step)
                        .limit(step)
                        .collect(collector)));
    }

    /**
     * 用于collection分批执行
     *
     * @param collection collection
     * @param step       批次容量
     * @param sync       是否同步
     * @param collector  分割收集器
     * @param consumer   consumer
     */
    public static <E, A, R> void batch(Collection<E> collection, int step, boolean sync, Collector<E, A, R> collector, Consumer<R> consumer) {
        if (CollectionsUtil.isEmpty(collection) || step <= 0) {
            return;
        }
        int total = collection.size() / step;
        if (collection.size() % step > 0) {
            total += 1;
        }
        batch(collection, step, total, sync, collector, consumer);
    }

    /**
     * 用于collection分批执行
     *
     * @param collection collection
     * @param total      总批次数
     * @param sync       是否同步
     * @param collector  分割收集器
     * @param consumer   consumer
     */
    public static <E, A, R> void batchTotal(Collection<E> collection, int total, boolean sync, Collector<E, A, R> collector, Consumer<R> consumer) {
        if (CollectionsUtil.isEmpty(collection) || total <= 0) {
            return;
        }
        int step = collection.size() / total;
        if (collection.size() % total > 0) {
            step += 1;
        }
        // 避免返回空集合
        int totalTemp = collection.size() / step;
        if (collection.size() % step > 0) {
            totalTemp += 1;
        }
        batch(collection, step, totalTemp, sync, collector, consumer);
    }

    /**
     * List分批同步执行
     * 固定每个批次的容量
     */
    public static <E> void batch(List<E> list, int step, Consumer<List<E>> consumer) {
        batch(list, step, true, Collectors.toList(), consumer);
    }

    /**
     * List分批执行
     * 固定每个批次的容量
     */
    public static <E> void batch(List<E> list, int step, boolean sync, Consumer<List<E>> consumer) {
        batch(list, step, sync, Collectors.toList(), consumer);
    }

    /**
     * Set分批同步执行
     * 固定每个批次的容量
     */
    public static <E> void batch(Set<E> set, int step, Consumer<Set<E>> consumer) {
        batch(set, step, true, Collectors.toSet(), consumer);
    }

    /**
     * Set分批执行
     * 固定每个批次的容量
     */
    public static <E> void batch(Set<E> set, int step, boolean sync, Consumer<Set<E>> consumer) {
        batch(set, step, sync, Collectors.toSet(), consumer);
    }

    /**
     * List分批同步执行
     * 固定总批次数
     */
    public static <E> void batchTotal(List<E> list, int total, Consumer<List<E>> consumer) {
        batchTotal(list, total, true, Collectors.toList(), consumer);
    }

    /**
     * List分批执行
     * 固定总批次数
     */
    public static <E> void batchTotal(List<E> list, int total, boolean sync, Consumer<List<E>> consumer) {
        batchTotal(list, total, sync, Collectors.toList(), consumer);
    }

    /**
     * Set分批同步执行
     * 固定总批次数
     */
    public static <E> void batchTotal(Set<E> set, int total, Consumer<Set<E>> consumer) {
        batchTotal(set, total, true, Collectors.toSet(), consumer);
    }

    /**
     * Set分批执行
     * 固定总批次数
     */
    public static <E> void batchTotal(Set<E> set, int total, boolean sync, Consumer<Set<E>> consumer) {
        batchTotal(set, total, sync, Collectors.toSet(), consumer);
    }
}
